"""pytest配置和共享fixtures"""

import asyncio
import pytest
import tempfile
import shutil
from pathlib import Path
from typing import Dict, Any, AsyncGenerator
from unittest.mock import AsyncMock, MagicMock

# 测试配置
TEST_CONFIG = {
    "database": {
        "redis": {
            "host": "localhost",
            "port": 6379,
            "db": 15,  # 使用测试数据库
            "password": None
        },
        "mongodb": {
            "host": "localhost",
            "port": 27017,
            "database": "mcp_memory_test",
            "username": None,
            "password": None
        },
        "neo4j": {
            "uri": "bolt://localhost:7687",
            "username": "neo4j",
            "password": "password123",
            "database": "test"
        },
        "chromadb": {
            "persist_directory": None,  # 将在测试中设置临时目录
            "collection_metadata": {
                "hnsw:space": "cosine"
            }
        }
    },
    "connection": {
        "stdio": {
            "enabled": True
        },
        "websocket": {
            "enabled": False,
            "host": "localhost",
            "port": 8765
        }
    },
    "logging": {
        "console_level": "DEBUG",
        "file_enabled": False
    },
    "memory": {
        "short_term_ttl": 3600,
        "long_term_cleanup_days": 30,
        "max_conversation_length": 50,
        "embedding_model": "all-MiniLM-L6-v2"
    }
}


@pytest.fixture(scope="session")
def event_loop():
    """创建事件循环"""
    loop = asyncio.new_event_loop()
    yield loop
    loop.close()


@pytest.fixture
def test_config():
    """测试配置"""
    return TEST_CONFIG.copy()


@pytest.fixture
def temp_dir():
    """临时目录"""
    temp_path = tempfile.mkdtemp()
    yield Path(temp_path)
    shutil.rmtree(temp_path, ignore_errors=True)


@pytest.fixture
def chromadb_temp_config(test_config, temp_dir):
    """ChromaDB临时配置"""
    config = test_config.copy()
    config["database"]["chromadb"]["persist_directory"] = str(temp_dir / "chromadb")
    return config


@pytest.fixture
def mock_redis():
    """Mock Redis客户端"""
    mock = AsyncMock()
    mock.ping.return_value = True
    mock.get.return_value = None
    mock.set.return_value = True
    mock.delete.return_value = 1
    mock.exists.return_value = False
    mock.keys.return_value = []
    mock.hget.return_value = None
    mock.hset.return_value = True
    mock.hgetall.return_value = {}
    mock.expire.return_value = True
    return mock


@pytest.fixture
def mock_mongodb():
    """Mock MongoDB客户端"""
    mock_collection = AsyncMock()
    mock_collection.insert_one.return_value = AsyncMock(inserted_id="test_id")
    mock_collection.find_one.return_value = None
    mock_collection.find.return_value = AsyncMock()
    mock_collection.update_one.return_value = AsyncMock(modified_count=1)
    mock_collection.delete_one.return_value = AsyncMock(deleted_count=1)
    mock_collection.count_documents.return_value = 0
    
    mock_db = AsyncMock()
    mock_db.__getitem__.return_value = mock_collection
    
    mock_client = AsyncMock()
    mock_client.__getitem__.return_value = mock_db
    
    return mock_client


@pytest.fixture
def mock_neo4j():
    """Mock Neo4j驱动"""
    mock_result = MagicMock()
    mock_result.single.return_value = None
    mock_result.data.return_value = []
    
    mock_session = AsyncMock()
    mock_session.run.return_value = mock_result
    
    mock_driver = AsyncMock()
    mock_driver.session.return_value.__aenter__.return_value = mock_session
    mock_driver.verify_connectivity.return_value = None
    
    return mock_driver


@pytest.fixture
def mock_chromadb():
    """Mock ChromaDB客户端"""
    mock_collection = MagicMock()
    mock_collection.add.return_value = None
    mock_collection.query.return_value = {
        "ids": [[]],
        "documents": [[]],
        "metadatas": [[]],
        "distances": [[]]
    }
    mock_collection.get.return_value = {
        "ids": [],
        "documents": [],
        "metadatas": []
    }
    mock_collection.update.return_value = None
    mock_collection.delete.return_value = None
    mock_collection.count.return_value = 0
    
    mock_client = MagicMock()
    mock_client.get_or_create_collection.return_value = mock_collection
    mock_client.list_collections.return_value = []
    
    return mock_client


@pytest.fixture
def mock_sentence_transformer():
    """Mock SentenceTransformer"""
    mock = MagicMock()
    mock.encode.return_value = [[0.1] * 384]  # 模拟384维向量
    return mock


@pytest.fixture
async def mock_database_manager(mock_redis, mock_mongodb, mock_neo4j, mock_chromadb):
    """Mock数据库管理器"""
    from src.database.manager import DatabaseManager
    
    manager = MagicMock(spec=DatabaseManager)
    manager.redis_client = mock_redis
    manager.mongodb_client = mock_mongodb
    manager.neo4j_driver = mock_neo4j
    manager.chromadb_client = mock_chromadb
    manager.is_initialized = True
    
    # 异步方法
    manager.initialize = AsyncMock()
    manager.shutdown = AsyncMock()
    manager.health_check = AsyncMock(return_value={
        "healthy": True,
        "databases": {
            "redis": {"status": "connected"},
            "mongodb": {"status": "connected"},
            "neo4j": {"status": "connected"},
            "chromadb": {"status": "connected"}
        }
    })
    
    return manager


@pytest.fixture
def sample_user_id():
    """示例用户ID"""
    return "test_user_123"


@pytest.fixture
def sample_memory_data():
    """示例记忆数据"""
    return {
        "content": "用户喜欢喝咖啡",
        "memory_type": "preference",
        "importance": 0.8,
        "metadata": {
            "category": "food",
            "source": "conversation"
        }
    }


@pytest.fixture
def sample_document_data():
    """示例文档数据"""
    return {
        "title": "机器学习基础",
        "content": "机器学习是人工智能的一个分支，它使计算机能够在没有明确编程的情况下学习。",
        "document_type": "article",
        "metadata": {
            "author": "测试作者",
            "category": "AI",
            "tags": ["机器学习", "人工智能"]
        }
    }


@pytest.fixture
def sample_conversation_data():
    """示例对话数据"""
    return {
        "messages": [
            {
                "role": "user",
                "content": "你好，我想了解机器学习",
                "timestamp": "2024-01-01T10:00:00Z"
            },
            {
                "role": "assistant",
                "content": "你好！我很乐意为你介绍机器学习。",
                "timestamp": "2024-01-01T10:00:01Z"
            }
        ],
        "metadata": {
            "topic": "机器学习",
            "language": "zh"
        }
    }


@pytest.fixture
def sample_mcp_request():
    """示例MCP请求"""
    return {
        "jsonrpc": "2.0",
        "id": 1,
        "method": "tools/call",
        "params": {
            "name": "memory_search",
            "arguments": {
                "user_id": "test_user_123",
                "query": "咖啡",
                "limit": 5
            }
        }
    }


# 测试标记
pytestmark = [
    pytest.mark.asyncio,
]


# 测试辅助函数
def assert_mcp_response(response: Dict[str, Any], expected_id: int = None):
    """验证MCP响应格式"""
    assert "jsonrpc" in response
    assert response["jsonrpc"] == "2.0"
    assert "id" in response
    
    if expected_id is not None:
        assert response["id"] == expected_id
    
    # 应该有result或error，但不能同时有
    has_result = "result" in response
    has_error = "error" in response
    assert has_result != has_error, "响应应该有result或error，但不能同时有"


def assert_mcp_error(response: Dict[str, Any], expected_code: int = None):
    """验证MCP错误响应"""
    assert_mcp_response(response)
    assert "error" in response
    
    error = response["error"]
    assert "code" in error
    assert "message" in error
    
    if expected_code is not None:
        assert error["code"] == expected_code


def assert_mcp_success(response: Dict[str, Any]):
    """验证MCP成功响应"""
    assert_mcp_response(response)
    assert "result" in response